//***************************************************************************************
//
//! \file ftp_server.c
//! Code for ftp server.
//!
//! \author    sc
//! \version   V1.0
//! \date      2022-11-19
//! \copyright GNU Public License V3.0
//
//***************************************************************************************
#ifndef FTP_SERVER
#define FTP_SERVER
#include <pthread.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include "ftp_server.h"
#include "config.h"
#include "cmd_server.h"
#define FTP_SERVER_PORT 21
#define USER_MODE 1
#define ANONYMOUS_MODE 2
Msg msg;
struct sockaddr_in server_addr, client_addr, addr_data;
char buffer[MAX_INFO];
char local_user[] = "admin";
char local_password[] = "123";
char anony_user[] = "anonymous";
char anony_password[] = "";
int ftp_data_sock;
int mode = -1;
struct ARG
{
    int client_sock;
    struct sockaddr_in client;
};
/// @brief server的欢迎语。
void greetings(void)
{
    printf("---FTP server is on now. Welcome!---\n");
}
/// @brief 创建一个TCP套接字。
/// @return 成功则返回套接字的描述符，失败则返回-1。
int create_socket(void)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("Failed to create socket");
        exit(1);
    }
    return sockfd;
}
/// @brief 绑定到指定端口上。若绑定不成功，将会直接退出程序。
/// @param server_addr 要绑定的本地端口
/// @param sockfd 要执行绑定的套接字
void bind_to_port(struct sockaddr_in server_addr, int sockfd)
{
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;                // match the socket() call
    server_addr.sin_port = htons(FTP_SERVER_PORT);   // specify the port to listen on
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // allow the server to accept a client connection on any interface
    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(int));
    if ((bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1))
    {
        perror("Error in binding");
        exit(1);
    }
}
/// @brief 使套接字监听本地指定端口。若监听失败，将直接退出。
/// @param sockfd 将要进行监听的套接字。
/// @param backlog 最多允许接收的连接请求数。
void listen_to_port(int sockfd, int backlog)
{
    if ((listen(sockfd, backlog)) == -1)
    {
        perror("Error in listening");
        exit(1);
    }
}
/// @brief 在当前监听的本地端口，接受来自外部的连接请求。
/// @param sock_listen 监听本地端口的套接字的描述符。
/// @return 成功则返回套接字描述符，失败则报错并返回-1。
int accept_connections(int sock_listen)
{
    int sockfd;
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    sockfd = accept(sock_listen, (struct sockaddr *)&client_addr, &len);
    if (sockfd == -1)
    {
        perror("Error in accepting the connection");
        return -1;
    }
    return sockfd;
}
/// @brief 向客户端发送信息。
/// @param socket_control 发送控制信令的套接字描述符。
/// @param info 要发送的信息（通常是预先定义好的RFC标准应答）
/// @param extra 附加信息
void send_message(int socket_control, char *info, char *extra)
{
    char c[MAX_INFO] = "";
    strcat(c, info);
    strncat(c, extra, MAX_INFO - strlen(info) - 1);
    if (send(socket_control, c, strlen(c), 0) == -1)
        perror("Error sending message");
}
/// @brief 接收客户端信息。
/// @param socket_control 发送控制信令的套接字描述符。
/// @return 若接收成功，则返回信息长度；失败则返回-1。
int recv_message(int socket_control)
{
    int num;
    if ((num = recv(socket_control, msg.control_info, MAX_INFO, 0)) == -1)
        perror("Error receiving message");
    return num;
}
/// @brief 验证给出的用户名是否存在。
/// @param username 客户端输入的用户名
/// @return 存在则返回登录模式，否则返回-1。
int validate_username(char *username)
{
    if (strncmp(username, local_user, MAX_INFO) == 0)
        return USER_MODE;
    else if (strncmp(username, anony_user, MAX_INFO) == 0)
        return ANONYMOUS_MODE;
    else
        return -1;
}
/// @brief 验证给出的密码是否匹配用户名。
/// @param password 客户端输入的密码
/// @param mode 登录模式
/// @return 存在则返回登录模式，否则返回-1。
int validate_password(char *password, int mode)
{
    printf("The mode is now:%d\n", mode);
    if (mode == USER_MODE && (strncmp(password, local_password, strlen(local_password)) == 0))
        return USER_MODE;
    else if (mode == ANONYMOUS_MODE && (strncmp(password, anony_password, strlen(anony_password)) == 0))
        return ANONYMOUS_MODE;
    else
        return -1;
}
/// @brief 处理用户登入的模块。如果用户没有正确选择以匿名或以帐户模式登录，则一直请求用户输入帐号密码，否则不允许后续操作。
/// @param socket_control 发送控制信令的套接字描述符。
void user_login(int socket_control)
{
    printf("User logging in...\n");
    char username[MAX_INFO];
    char password[MAX_INFO];
    send_message(socket_control, serverInfo220, "\nPlease enter the username. (Enter anonymous to log in as a visitor)\n");
    send_message(socket_control, "task done", "\0");
    while (1)
    {
        bzero(msg.control_info, MAX_INFO);
        bzero(username, MAX_INFO);
        bzero(password, MAX_INFO);
        if (recv_message(socket_control) == -1)
        {
            send_message(socket_control, serverInfo, "");
            continue;
        }
        for (int i = 5, n = 0; msg.control_info[i] != 0; i++, n++)
            username[n] = msg.control_info[i];
        printf("username:%s\n", username);
        if ((mode = validate_username(username)) == -1)
        {
            send_message(socket_control, serverInfo, "\rUsername does not exist.\n");
            continue;
        }
        else
        {
            bzero(msg.control_info, MAX_INFO);
            send_message(socket_control, serverInfo331, "(Press enter if you're in anonymous mode)\n");
        }
        send_message(socket_control, "task done", "\0");

        if ((recv_message(socket_control)) == -1)
        {
            send_message(socket_control, serverInfo, "");
            continue;
        }

        for (int i = 5, n = 0; msg.control_info[i] != 0; i++, n++)
            password[n] = msg.control_info[i];
        if ((mode = validate_password(password, mode)) == -1)
        {
            // printf("password:%s\n", password);
            send_message(socket_control, serverInfo, "");
            continue;
        }
        else if (mode == USER_MODE)
            send_message(socket_control, serverInfo230, "\rYou're logged in as a user now.\n");
        else if (mode == ANONYMOUS_MODE)
            send_message(socket_control, serverInfo230, "\rYou're logged in as a visitor now.\n");
        printf("Successful logged in.\n");
        send_message(socket_control, "task done", "\0");
        return;
    }
}
/// @brief 反复处理用户端发送的指令。
/// @param socket_control 发送控制信令的套接字描述符。
void command_handler(int socket_control)
{
    bzero(msg.control_info, MAX_INFO);
    msg.file_type = BINFILE;
    msg.mode = PASV;
    printf("msg:%s\n", msg.control_info);
    while (recv_message(socket_control) != -1)
    {
        printf("msg:%s\n", msg.control_info);
        if (strncmp("quit", msg.control_info, 4) == 0 || strncmp("QUIT", msg.control_info, 4) == 0)
        {
            send_message(socket_control, serverInfo221, "");
            break;
        }
        else if (strncmp("pasv", msg.control_info, 4) == 0 || strncmp("PASV", msg.control_info, 4) == 0)
        {
            printf("building pasv connection...\n");
            handle_pasv(socket_control, &msg);
        }
        else if (strncmp("port", msg.control_info, 4) == 0 || strncmp("PORT", msg.control_info, 4) == 0)
        {
            printf("building port connection...\n");
            handle_port(socket_control, &msg);
        }
        else if (strncmp("close", msg.control_info, 5) == 0 || strncmp("CLOSE", msg.control_info, 5) == 0)
        {
            printf("Client quit!\n");
            if (shutdown(socket_control, SHUT_WR) == -1)
                perror("Fail to shutdown\n");
        }
        else if (strncmp("pwd", msg.control_info, 3) == 0 || strncmp("PWD", msg.control_info, 3) == 0)
        {
            server_pwd(socket_control, &msg);
        }
        else if (strncmp("cwd", msg.control_info, 3) == 0 || strncmp("CWD", msg.control_info, 3) == 0)
        {
            server_cwd(socket_control, &msg);
        }
        else if (strncmp("mkd", msg.control_info, 3) == 0 || strncmp("MKD", msg.control_info, 3) == 0)
        {
            server_mkd(socket_control, &msg);
        }
        else if (strncmp("rmd", msg.control_info, 3) == 0 || strncmp("RMD", msg.control_info, 3) == 0)
        {
            server_rmd(socket_control, &msg);
        }
        else if (strncmp("dele", msg.control_info, 4) == 0 || strncmp("DELE", msg.control_info, 4) == 0)
        {
            server_del(socket_control, &msg);
        }
        else if (strncmp("list", msg.control_info, 4) == 0 || strncmp("LIST", msg.control_info, 4) == 0)
        {
            server_list(socket_control, &msg);
            close(msg.ftp_data_sock);
        }
        else if (strncmp("stor", msg.control_info, 4) == 0 || strncmp("STOR", msg.control_info, 4) == 0)
        {
            server_put(socket_control, &msg);
            close(msg.ftp_data_sock);
        }
        else if ((strncmp("retr", msg.control_info, 4) == 0) || (strncmp("RETR", msg.control_info, 4) == 0))
        {
            server_get(socket_control, &msg);
            close(msg.ftp_data_sock);
        }
        else
            send_message(socket_control, serverInfo, "\rPlease try again.\n");
        bzero(msg.control_info, MAX_INFO);
        send_message(socket_control, "task done", "\0");
    }
}
/// @brief 处理用户的请求，包括登录和命令处理。
/// @param arg 含发送控制信令的套接字描述符。
void handle_client_request(void *arg)
{
    int socket_control = ((struct ARG *)arg)->client_sock;
    printf("Start handling user request...\n");
    user_login(socket_control);
    command_handler(socket_control);
    printf("Goodbye!\n");
    close(socket_control);
    pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
    int socket_listen, socket_control;
    pthread_t thread;
    struct ARG arg;

    greetings();
    socket_listen = create_socket();
    bind_to_port(server_addr, socket_listen);
    listen_to_port(socket_listen, 5);
    while (1)
    {
        if ((socket_control = accept_connections(socket_listen)) == -1)
        {
            printf("Accept error!\n");
            exit(1);
        }
        arg.client_sock = socket_control;
        memcpy((void *)&arg.client, &client_addr, sizeof(client_addr));
        if (pthread_create(&thread, NULL, handle_client_request, (void *)&arg))
        {
            perror("Thread create error!");
            exit(1);
        }
    }
    close(socket_listen);
    return 0;
}
#endif